// network.cpp
#include "network.h"
#include "../character/ascii.h"
#include "../include/HashTable.h"
#include "stdio.h"

int NETWORKAPI_Ip6Addr2String(U8 *pu8_Ip, OUT char *pcOut)
{
    int iRet = -1, i;
    int iResult, iTemp;
    char *pcWrite           = pcOut;
    static char s_cSplit[8] = ":::::::";

    for(i = 0; i < 8; i++)
    {
        ::sprintf(pcOut + i * 5, "%02X%02X", pu8_Ip[i << 1], pu8_Ip[(i << 1) + 1]);
        pcOut[i * 5 + 4] = s_cSplit[i];
    }
    iRet = 0;
_Exit:
    return iRet;
}

U16 NETWORKAPI_TcpCheckSum(U32 u32_Ip1, U32 u32_Ip2, char *pcTcpData, U16 u16_TcpDataLen)
{
    U32 u32_Ret    = 0;
    U16 *pu16_Temp = (U16 *)pcTcpData;
    int i;
    int iU16Num = u16_TcpDataLen / 2;

    for(i = 0; i < iU16Num; i++) u32_Ret += pu16_Temp[i];
    if(u16_TcpDataLen % 2) u32_Ret += (U8)pcTcpData[u16_TcpDataLen - 1];
    u32_Ret += u32_Ip1 >> 16;
    u32_Ret += u32_Ip2 >> 16;
    u32_Ret += u32_Ip1 & 0xFFFF;
    u32_Ret += u32_Ip2 & 0xFFFF;
    u32_Ret += htons(u16_TcpDataLen);
    u32_Ret += htons(PROTO_IP_TCP);
    while(u32_Ret >> 16) u32_Ret = (u32_Ret >> 16) + (u32_Ret & 0xFFFF);
_Exit:
    return ~((U16)u32_Ret);
}

U16 NETWORKAPI_TcpCheckSum(U8 u8_IpArry1[16], U8 u8_IpArry2[16], char *pcTcpData, U16 u16_TcpDataLen)
{
    U32 u32_Ret = 0;
    U16 *pu16_Temp;
    int i;
    int iU16Num = u16_TcpDataLen / 2;

    pu16_Temp = (U16 *)pcTcpData;
    for(i = 0; i < iU16Num; i++) u32_Ret += pu16_Temp[i];
    if(u16_TcpDataLen % 2) u32_Ret += (U8)pcTcpData[u16_TcpDataLen - 1];

    pu16_Temp = (U16 *)u8_IpArry1;
    for(i = 0; i < 8; i++) u32_Ret += pu16_Temp[i];
    pu16_Temp = (U16 *)u8_IpArry2;
    for(i = 0; i < 8; i++) u32_Ret += pu16_Temp[i];

    u32_Ret += htons(u16_TcpDataLen);
    u32_Ret += htons(PROTO_IP_TCP);
    while(u32_Ret >> 16) u32_Ret = (u32_Ret >> 16) + (u32_Ret & 0xFFFF);
_Exit:
    return ~((U16)u32_Ret);
}

int NETWORKAPI_AnalyChunk(IN char *pcIn, IN int &iLenIn, OUT int &iChunk, OUT int &iChunkLen)
{
    int iRet = -1, i;
    int iResult, iTemp;

    if(pcIn == NULL || iLenIn <= 0) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS);

    iChunk = 0;
    while(iChunk < iLenIn && ASCIIAPI_IsHex(pcIn[iChunk])) iChunk++;
    if(iChunk > iLenIn - 2 || iChunk > 7) SET_RET_AND_GOTO_EXIT(NETWORK_ERR_INVALID_CHUNK);

    iChunkLen = 0;
    for(i = 0; i < iChunk; i++) iChunkLen = (iChunkLen << 4) + g_u8aAsciiVal[pcIn[i]];
    if(iChunk + iChunkLen + 4 > iLenIn) SET_RET_AND_GOTO_EXIT(NETWORK_ERR_INVALID_CHUNK);
    if(COERCION_AND_COMPARE("\r\n", pcIn + iChunk, !=, U16) || COERCION_AND_COMPARE("\r\n", pcIn + iChunk + iChunkLen + 2, !=, U16)) SET_RET_AND_GOTO_EXIT(NETWORK_ERR_INVALID_CHUNK);

    iChunk += 2;
    iLenIn = iChunk + iChunkLen + 2;

    iRet = 0;
_Exit:
    return iRet;
}

int NETWORKAPI_HttpDechunk(IN char *pcIn, IN OUT int &iLenIn, OUT char *pcOut, IN OUT int &iLenOut)
{
    int iRet = -1, i;
    int iResult, iTemp;
    int iRead = 0, iWrite = 0;
    int iChunk, iChunkLen;

    if(pcIn == NULL || iLenIn <= 0) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS);
    if(pcOut && iLenOut <= 0) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS);

    while(iRead < iLenIn)
    {
        iTemp   = iLenIn - iRead;
        iResult = NETWORKAPI_AnalyChunk(pcIn + iRead, iTemp, iChunk, iChunkLen);
        if(iResult == 0)
        {
            if(iWrite + iChunkLen > iLenOut) SET_RET_AND_GOTO_EXIT(COMMON_ERR_NOMEM);
            if(iChunkLen > 0)
            {
                memcpy(pcOut + iWrite, pcIn + iRead + iChunk, iChunkLen);
                iWrite += iChunkLen;
                iRead += iTemp;
            }
            else if(iChunkLen == 0)
            {
                iRead += iTemp;
                goto _Complete;
            }
            else goto _Exit;
        }
        else if(iResult == NETWORK_ERR_INVALID_CHUNK)
        {
            if(iRead == 0) SET_RET_AND_GOTO_EXIT(NETWORK_ERR_INVALID_CHUNK);
            else goto _Complete;
        }
        else SET_RET_AND_GOTO_EXIT(iResult);
    }
_Complete:
    iLenIn  = iRead;
    iLenOut = iWrite;
    iRet    = 0;
_Exit:
    return iRet;
}

int NETWORKAPI_InitSocket()
{
    int iRet = -1;
#ifdef WINDOWS
    int iResult;
    WORD wVersionRequested;
    WSADATA wsaData;

    wVersionRequested = MAKEWORD(2, 2);

    iResult = WSAStartup(wVersionRequested, &wsaData);
    // if ( iResult != 0 ) {
    /* Tell the user that we could not find a usable */
    /* WinSock DLL.                                  */
    // goto _Exit;
    //}

    /* Confirm that the WinSock DLL supports 2.2.*/
    /* Note that if the DLL supports versions greater    */
    /* than 2.2 in addition to 2.2, it will still return */
    /* 2.2 in wVersion since that is the version we      */
    /* requested.                                        */

    // if ( LOBYTE( wsaData.wVersion ) != 2 ||
    //	HIBYTE( wsaData.wVersion ) != 2 ) {
    //		/* Tell the user that we could not find a usable */
    //		/* WinSock DLL.                                  */
    //		WSACleanup( );
    //		goto _Exit;
    // }
#endif

    iRet = 0;
_Exit:
    return iRet;
}
int NETWORKAPI_CleanSocket()
{
    int iRet = 0;
#ifdef WINDOWS
    WSACleanup();
#endif
_Exit:
    return iRet;
}

static U16 gs_u16PppFcsAry[256] = { // FCS lookup table.计算PPP校验和用
    0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
    0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
    0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
    0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
    0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
    0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
    0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
    0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
    0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
    0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
    0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78};

int NETWORKAPI_Find1stPpp(IN U8 *pu8_Data, IN OUT int &iDataLen, OUT int &iStart)
{
    int iRet = -1, i;
    int iResult, iTemp;
    int iState = 0;

    if(pu8_Data == NULL || iDataLen <= 0) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS);

    for(i = 0; i < iDataLen; i++)
    {
        if(iState)
        {
            if(pu8_Data[i] == 0x7E)
            {
                iTemp = i - iStart;
                if(iTemp >= 7)
                {
                    iDataLen = iTemp;
                    iState   = 2;
                    break;
                }
                else iStart = i;
            }
        }
        else
        {
            if(pu8_Data[i] == 0x7E)
            {
                iStart = i;
                iState = 1;
            }
        }
    }

    if(iState == 2) iRet = 0;
    else iRet = NETWORK_WARN_NOTFOUND;
_Exit:
    return iRet;
}

static inline int PppEsc(IN U8 u8_Data, IN OUT U8 *&pu8_Write, IN U8 *pu8_End)
{ // 转义
    if(u8_Data < 0x20 || u8_Data == 0x7E || u8_Data == 0x7D)
    {
        if(pu8_Write + 1 >= pu8_End) return COMMON_ERR_NOMEM;
        pu8_Write[0] = 0x7D;
        pu8_Write[1] = u8_Data ^ 0x20;
        pu8_Write += 2;
    }
    else
    {
        if(pu8_Write >= pu8_End) return COMMON_ERR_NOMEM;
        pu8_Write[0] = u8_Data;
        pu8_Write += 1;
    }
    return 0;
}

static inline int PppCse(OUT U8 &u8_Data, IN OUT U8 *&pu8_Read, IN U8 *pu8_End)
{ // 反转义
    if(pu8_Read >= pu8_End) return 1;
    if(pu8_Read[0] == 0x7D)
    {
        if(pu8_Read + 1 >= pu8_End) return -1;
        u8_Data = pu8_Read[1] ^ 0x20;
        pu8_Read += 2;
    }
    else
    {
        u8_Data = pu8_Read[0];
        pu8_Read += 1;
    }
    return 0;
}
int NETWORKAPI_CstctPppFrame(IN U8 *pu8_Data, IN int iDataLen, IN U16 u16_Proto, OUT U8 *pu8_Frame, IN OUT int &iFrameLen)
{
    int iRet = -1, i;
    int iResult, iTemp;
    PPP_HEADER stHdr;
    PPP_TAILER stTailer;
    U8 *pu8_Write = pu8_Frame, *pu8_End = pu8_Frame + iFrameLen, *pu8_Temp;
    U16 u16_CheckSum;

    if((pu8_Data != NULL && iDataLen <= 0) || pu8_Frame == NULL || iFrameLen <= sizeof(PPP_HEADER) + sizeof(PPP_TAILER) || ((u16_Proto & 0x11) != 0x01)) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS);
    // 填写头部//[
    stHdr.u8_Head      = 0x7E;
    stHdr.u8_Addr      = 0xFF;
    stHdr.u8_Ctrl      = 0x03;
    stHdr.u16_Protocol = htons(u16_Proto);
    //]
    // 计算效验和//[
    u16_CheckSum = NETWORKAPI_PPPFCS16(PPPINITFCS16, (U8 *)&stHdr.u8_Addr, 4);
    if(pu8_Data) u16_CheckSum = NETWORKAPI_PPPFCS16(u16_CheckSum, pu8_Data, iDataLen);
    u16_CheckSum ^= 0xFFFF;
    //]
    // 转义输出//[
    pu8_Write[0] = stHdr.u8_Head;
    pu8_Write++;
    pu8_Temp = (U8 *)(&stHdr.u8_Addr);
    for(i = 0; i < 4; i++)
    {
        iResult = PppEsc(pu8_Temp[i], pu8_Write, pu8_End);
        if(iResult < 0) SET_RET_AND_GOTO_EXIT(iResult);
    }
    if(pu8_Data == NULL) iDataLen = 0;
    for(i = 0; i < iDataLen; i++)
    {
        iResult = PppEsc(pu8_Data[i], pu8_Write, pu8_End);
        if(iResult < 0) SET_RET_AND_GOTO_EXIT(iResult);
    }
    iResult = PppEsc(u16_CheckSum & 0x00FF, pu8_Write, pu8_End);
    if(iResult < 0) SET_RET_AND_GOTO_EXIT(iResult);
    iResult = PppEsc(u16_CheckSum >> 8, pu8_Write, pu8_End);
    if(iResult < 0) SET_RET_AND_GOTO_EXIT(iResult);
    if(pu8_Write >= pu8_End) SET_RET_AND_GOTO_EXIT(COMMON_ERR_NOMEM);
    pu8_Write[0] = stHdr.u8_Head;
    pu8_Write++;
    //]

    iFrameLen = pu8_Write - pu8_Frame;
    iRet      = 0;
_Exit:
    return iRet;
}

int NETWORKAPI_ParsePppFrame(IN U8 *pu8_Frame, IN int iFrameLen, OUT U8 *pu8_Data, IN OUT int &iDataLen, OUT U16 &u16_Proto, OUT U16 &u16_CheckSum)
{
    int iRet = -1, i;
    int iResult, iTemp;
    PPP_HEADER stHdr;
    int iDataLenE = iFrameLen - 7;
    U8 *pu8_Temp;
    U8 *pu8_Read = pu8_Frame + 1, *pu8_ReadEnd = pu8_Frame + iFrameLen;

    if(pu8_Data == NULL || pu8_Frame == NULL || iFrameLen < 7 || iDataLen <= 0) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS);
    // if(pu8_Frame[0]!=0x7E) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS);
    // 头//[
    pu8_Temp = (U8 *)&stHdr.u8_Addr;
    for(i = 0; i < 4; i++)
    {
        iResult = PppCse(pu8_Temp[i], pu8_Read, pu8_ReadEnd);
        if(iResult) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS);
    }
    u16_Proto = ntohs(stHdr.u16_Protocol);
    //]
    // 数据//[
    for(i = 0; i < iDataLen; i++)
    {
        iResult = PppCse(pu8_Data[i], pu8_Read, pu8_ReadEnd);
        if(iResult) break;
    }
    if(i >= iDataLen && pu8_Read < pu8_ReadEnd) SET_RET_AND_GOTO_EXIT(COMMON_ERR_NOMEM);
    iDataLen = i;
    if(iDataLen < 2) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS);
    //]
    // 计算效验和//[
    u16_CheckSum = NETWORKAPI_PPPFCS16(PPPINITFCS16, (U8 *)&stHdr.u8_Addr, 4);
    u16_CheckSum = NETWORKAPI_PPPFCS16(u16_CheckSum, pu8_Data, iDataLen);
    iDataLen -= 2;
    //]
    iRet = 0;
_Exit:
    return iRet;
}

U16 NETWORKAPI_PPPFCS16(U16 u16_Fcs, U8 *pu8_Data, int iLen)
{
    while(iLen--) u16_Fcs = (u16_Fcs >> 8) ^ gs_u16PppFcsAry[(u16_Fcs ^ *pu8_Data++) & 0xff];
    return u16_Fcs;
}

inline static int FindAndParsePppFrame_Write(IN U8 u8_Data, IN OUT int &iHdrAndDataLen, IN OUT U8 *&pu8_Write, IN U8 *pu8_End, IN OUT U16 &u16_Proto, IN OUT U16 &u16_CheckSum)
{
    int iRet = -1, i;
    int iResult, iTemp;
    U16 u16_Temp;

    if(pu8_Write >= pu8_End) SET_RET_AND_GOTO_EXIT(COMMON_ERR_NOMEM);

    u16_CheckSum = (u16_CheckSum >> 8) ^ gs_u16PppFcsAry[(u16_CheckSum ^ u8_Data) & 0xff];
    iHdrAndDataLen++;
    if(iHdrAndDataLen > 4)
    {
        *pu8_Write = u8_Data;
        pu8_Write++;
    }
    else if(iHdrAndDataLen == 4) u16_Proto = (u16_Proto & 0xFF00) | u8_Data;
    else if(iHdrAndDataLen == 3)
    {
        u16_Temp  = u8_Data;
        u16_Proto = (u16_Proto & 0x00FF) | (u16_Temp << 8);
    }

    iRet = 0;
_Exit:
    return iRet;
}

int NETWORKAPI_FindAndParsePppFrame(IN U8 *pu8_Frame, IN OUT int &iFrameLen, OUT U8 *pu8_Data, IN OUT int &iDataLen, OUT U16 &u16_Proto, OUT U16 &u16_CheckSum)
{
    int iRet = -1, i;
    int iResult, iTemp;
    int iState = 0;     // 0开始，1找到0x7E，2找到0x7E并处于转义状态，3找到结尾。
    int iEnd   = 0,
        iHdrAndDataLen; // 反转义的数据长度
    U8 *pu8_Write;
    U8 *pu8_End = pu8_Data + iDataLen;

    if(pu8_Data == NULL || pu8_Frame == NULL || iFrameLen < 7 || iDataLen <= 0) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS);

    for(i = 0; i < iFrameLen; i++)
    {
        switch(iState)
        {
        case 0:
            if(iFrameLen - i < 8) goto _End;
            if(pu8_Frame[i] == 0x7E)
            {
                iHdrAndDataLen = 0;
                u16_CheckSum   = PPPINITFCS16;
                pu8_Write      = pu8_Data;
                iState         = 1;
            }
            break;
        case 1:
            if(pu8_Frame[i] == 0x7E)
            {
                iEnd   = i;
                iState = 3;
                break;
            }
            if(pu8_Frame[i] != 0x7D)
            {
                iResult = FindAndParsePppFrame_Write(pu8_Frame[i], iHdrAndDataLen, pu8_Write, pu8_End, u16_Proto, u16_CheckSum);
                if(iResult < 0) SET_RET_AND_GOTO_EXIT(iResult);
            }
            else iState = 2;
            break;
        case 2:
            if(pu8_Frame[i] == 0x7E)
            {
                iEnd   = i;
                iState = 3;
                break;
            }
            iResult = FindAndParsePppFrame_Write(pu8_Frame[i] ^ 0x20, iHdrAndDataLen, pu8_Write, pu8_End, u16_Proto, u16_CheckSum);
            if(iResult < 0) SET_RET_AND_GOTO_EXIT(iResult);
            iState = 1;
            break;
        case 3:
            if(iHdrAndDataLen < 6) iState = 1;
            else goto _End;
        default:
            goto _Exit;
            break;
        }
    }
_End:
    if(iState == 3 && iHdrAndDataLen >= 6)
    {
        iFrameLen = iEnd;
        iDataLen  = iHdrAndDataLen - 6;
        iRet      = 0;
    }
    else iRet = NETWORK_WARN_NOTFOUND;
_Exit:
    return iRet;
}
